/**
 * 
 */
package com.bloomingstars.sqm.orm;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.CopyOnWriteArrayList;

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.cfg.Environment;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.service.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.service.spi.Configurable;
import org.hibernate.service.spi.Stoppable;

import com.bloomingstars.sqm.util.StackTraceLogger;

/**
 * @author Emmanuele Sordini
 */
public class HibernateConnectionProviderH4 implements ConnectionProvider,
        Stoppable, Configurable {

    private static final long serialVersionUID = -5524186214344458824L;

    private final static Logger log = LogManager.getLogger(HibernateConnectionProviderH4.class);

    private boolean autocommit;
    private Integer isolation;
    private String jdbcUrl;
    private Properties connectionProps = new Properties();
    private List<ConnectionInfo> openConnectionList = 
        new CopyOnWriteArrayList<ConnectionInfo>();
    private boolean verboseLoggingFlag;    
    
    @SuppressWarnings("rawtypes")
    @Override
    public boolean isUnwrappableAs(Class arg0) {
        log.info("In isUnwrappableAs() method");
        return false;
    }

    @Override
    public <T> T unwrap(Class<T> arg0) {
        log.info("In unwrap() method");
        return null;
    }

    @SuppressWarnings("rawtypes")
    @Override
    public void configure(Map props) {
        String jdbcDriverClass = (String) props.get(Environment.DRIVER);
        jdbcUrl = (String) props.get(Environment.URL);

        log.info("Using driver: " + jdbcDriverClass + " at URL: " + jdbcUrl);
        log.info("Connection url: " + (String) props.get(Environment.URL));
        log.info("Connection username " + (String) props.get(Environment.USER));
        log.info("autocommit mode: " + (String) props.get(Environment.AUTOCOMMIT));
        connectionProps.setProperty("username", (String) props.get(Environment.USER));
        connectionProps.setProperty("password", (String) props.get(Environment.PASS));

        if (jdbcDriverClass == null) {
            log.warn("No JDBC Driver class was specified by property " + Environment.DRIVER);
        } else {
          try {
              Class.forName(jdbcDriverClass);
          } catch (ClassNotFoundException cnfe) {
              try {
                  ReflectHelper.classForName(jdbcDriverClass);
              } catch (ClassNotFoundException e) {
                  String msg = "JDBC Driver class not found: "
                          + jdbcDriverClass;
                  log.error(msg, e);
                  throw new HibernateException(msg, e);
              }
          }
      }

      String i = (String) props.get(Environment.ISOLATION);
      if (i == null) {
          isolation = null;
      } else {
          isolation = new Integer(i);
          log.info("JDBC isolation level: "
                  + Environment.isolationLevelToString(isolation.intValue()));
      }

//      setConfiguredAttributes();        
    }

    @Override
    public void stop() {
        log.info("Stopping WebAriadneConnectionProviderH4");
    }

    @Override
    public void closeConnection(Connection conn) throws SQLException {
        log.info("Closing connection: <" + conn.toString() + ">");
        conn.close();

//        synchronized (openConnectionList) {
            ConnectionInfo ci = findConnection(conn);
            if (ci != null) {
                openConnectionList.remove(ci);
            }
            log.info("Total number of open connections: " + openConnectionList.size());
            logConnectionList();            
//        }
    }

    @Override
    public Connection getConnection() throws SQLException {
        if (jdbcUrl != null) {
            Connection c = DriverManager.getConnection(jdbcUrl, connectionProps.getProperty("username"), connectionProps.getProperty("password"));
            if (isolation != null) {
                c.setTransactionIsolation(isolation.intValue());
            }
            if (c.getAutoCommit() != autocommit) {
                c.setAutoCommit(autocommit);
            }

//            synchronized(openConnectionList) {
                openConnectionList.add(new ConnectionInfo(c, new Exception(), System.currentTimeMillis()));
                log.info("Opening connection: <" + c.toString() + ">");
                log.info("Total number of open connections: " + openConnectionList.size());
                logConnectionList();                
//            }

            return c;
        } else {
            throw new SQLException("JDBC driver not properly initialized");
        }
    }

    @Override
    public boolean supportsAggressiveRelease() {
        return false;
    }

    private void logConnectionList() {
        log.info("****LIST OF OPEN CONNECTIONS STARTS****");
        for (ConnectionInfo ci: openConnectionList) {            
            log.info(ci.getConnection().toString());            
            if (verboseLoggingFlag) {
                StackTraceLogger stackTraceLogger = new StackTraceLogger(log);
                stackTraceLogger.logStackTrace(Level.DEBUG, "org.jrc", ci.getStackTrace());                
            }
        }
        log.info("****LIST OF OPEN CONNECTIONS ENDS****");        
    }

    private ConnectionInfo findConnection(Connection c) {
        for (ConnectionInfo ci: openConnectionList) {
            if (ci.getConnection() == c) {
                return ci;
            }
        }
        log.warn("Connection <" + c.toString() + "> not found in list of active connections");
        return null;
    }
    
//    private void setConfiguredAttributes() {
//        AppConfiguration appConfiguration = ResourceFactory.instance().getAppConfiguration();
//        verboseLoggingFlag = false;
//        String s = appConfiguration
//                .getProperty(AppConfigurationType.DB_CONNECTION_VERBOSE_LOGGING);
//        if (s != null) {
//            verboseLoggingFlag = 
//                s.equalsIgnoreCase("yes") || s.equalsIgnoreCase("true") ? true : false;
//        }
//    }    
}
